package com.epam.iunna.bookshop.controller.utils.dispatch;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;

import com.epam.iunna.bookshop.controller.actions.ShowJSP;
import com.epam.iunna.bookshop.controller.application.App;
import com.epam.iunna.bookshop.controller.utils.StringUtils;

/**
 * Класс фабрики для создания объектов интерфейса ActionInt.
 * 
 * 
 */
public final class ActionFactory {
    /**
     * Таблица пар действие=класс_действия.
     */
    private static HashMap map = new HashMap();
    /**
     * Указатель на единственный экземпляр класса ActionFactory.
     */
    private static ActionFactory instance = null;

    /**
     * Конструктор класса.
     */
    private ActionFactory() {
        init();
    }

    /**
     * Метод создания ActionFactory таким образом чтоб всегда существовал только
     * один экземпляр класса.
     * 
     * @return обьект ActionFactory.
     */
    public static ActionFactory getInstance() {
        if (instance == null) {
            instance = new ActionFactory();
        }
        return instance;
    }

    /**
     * Выбирает из массива actions все елементы шаблон которых соответствует
     * строке queryString.
     * 
     * @param actions
     *            Массив свойств в котором будут искаться соответствия.
     * @param queryString
     *            цель поиска.
     * @return массив свойств в которых были найдены соответствия.
     */
    private static Properties[] select(final Properties[] actions,
            final String queryString) {
        ArrayList<Properties> result = new ArrayList<Properties>(); // создаем список который будем возвращять
        if (actions != null && queryString != null ) { 
            //и список в котором будем искать не пустой
            for (int i = 0; i < actions.length; i++) { // для каждого элемента списка действий 
                if (StringUtils.compare(queryString, actions[i].getProperty("pattern"))) { // сравниваем на соответствие шаблон действия с заданой строкой
                    result.add(actions[i]); // если совпало то добовляем в масив результата
                }
            }
        }
        return result.toArray(new Properties[result.size()]); //масив действий совпадающих по шаблону со строкой
    }

    /**
     * Метод получает из файла настройки все возможные действия. Выбирает из них
     * те, паттерн которых совпадает со строкой queryString(используя регулярные
     * выражения). Если пользователь не имеет сессии, то из набора полученных
     * действий в виде объектов Properties, выбирается тот где параметр
     * grant-for не указан или пустой(то есть выполнение разрешено всем). Если
     * же у пользователя есть права , то выбирается то действие которое
     * разрешено пользователю. Далее из таблицы(заполненной в методе init()) map
     * выбирается класс соответсвующий данному действию, создается обьект этого
     * класса и возвращается в результате.
     * 
     * @param queryString
     *            строка с указанным действием
     * @param session
     *            сессия пользователя
     * @return обьект реализующий интерфейс ActionInt (события).
     */
    public ActionInt createAction(final String queryString,
            final String userRole) {
    	
        Properties[] actions = select(App.config.getProperties(".//controller/dispatch"), queryString); //получаем в список из файла настройки все действия,
        // выбераем из них те которые подходят по шаблону со строкой queryString

        try {
            if (userRole == null) { // если сесии не существует 
                for (int i = 0; i < actions.length; i++) { // начиная с первого элемента списка
                    if (actions[i].getProperty("grant-for") == null
                            || actions[i].getProperty("grant-for").equals("")) { // если права для действия не заданы
                        if ( actions[i].getProperty("jsp") != null && !actions[i].getProperty("jsp").equals("") ) { // если для действия определена jsp страница
                            return new ShowJSP(actions[i].getProperty("jsp")); //возвращаем обьект с ее адресом , первого найденного действия для которого не указаны права
                        } 
                        return (ActionInt)((Class)map.get(actions[i].getProperty("action"))).newInstance(); //возвращаем обьект первого найденного действия для которого не указаны права
                    }
                }
                return null;
            }

            for (int i = 0; i < actions.length; i++) { // если сессия существует , начиная с первого элемента списка
                if (actions[i].getProperty("grant-for") != null
                        && !actions[i].getProperty("grant-for").equals("")) { // если доступ к действию ограниичен
                    String[] cats = actions[i].getProperty("grant-for").split(","); //получаем список для кого разрешено действие
                    
                    for (int j = 0; j < cats.length; j++) {
                        if (cats[j].equals( userRole )) { // если права пользователя совпали с требованиями действия
                            
                            if (!actions[i].getProperty("jsp").equals("") && actions[i].getProperty("jsp") != null) { // если для действия определена jsp страница
                                return new ShowJSP(actions[i].getProperty("jsp")); //возвращаем обьект действия получая из map по строке с названием класса класс обьекта который надо создать 
                            }
                            
                            Class actionClass = (Class)map.get(actions[i].getProperty("action"));
                            return (ActionInt)actionClass.newInstance(); //возвращаем обьект действия получая из map по строке с названием класса класс обьекта который надо создать  
                        }
                    }
                } else { //если сессия существует и доступ к действию не ограниичен
                    if ( actions[i].getProperty("jsp") != null && !actions[i].getProperty("jsp").equals("")) { // если для действия определена jsp страница
                        return new ShowJSP(actions[i].getProperty("jsp")); //возвращаем обьект с ее адресом
                    }
                    return (ActionInt)((Class)map.get(actions[i].getProperty("action"))).newInstance(); //возвращаем обьект действия получая из map по строке с названием класса класс обьекта который надо создать 
                }
            }
        } catch (InstantiationException e) {
            e.printStackTrace(); // выводим ошибки в консоль
        } catch (IllegalAccessException e) {
            e.printStackTrace(); // выводим ошибки в консоль
        }
        return null;
    }

    /**
     * инициализация фабрики. Сохраняет в HashMap пары действие-класс, которые
     * берет из конфигурации.
     */
    public static void init() {
        String[] actions = App.config.getValues(".//controller/dispatch/@action"); // записываем в масив названия класов всех дейсвий 
        for (int i = 0; i < actions.length; i++) {
            try {
                if (!actions[i].equals("")) {
                    map.put(actions[i], Class.forName(actions[i])); // сохраняет в HashMap пары имя_класса(строка)=класс
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
}
